package org.gnomus.data;

import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;
import com.google.appengine.api.datastore.*;
import org.gnomus.util.json;
import org.gnomus.util.misc;
import org.gnomus.util.strings;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

public class store {

    private static final DatastoreService dss = DatastoreServiceFactory.getDatastoreService();
    private static final BlobstoreService bss = BlobstoreServiceFactory.getBlobstoreService();
    private static final Key root = KeyFactory.createKey("gnomus", "data");

    public static Transaction beginTransaction() {
        return dss.beginTransaction();
    }

    public static void put(Entity e) {
        dss.put(e);
    }

    public static Key put(String kind, Map<String, Object> properties) {
        Entity e = new Entity(kind, root);
        for (Map.Entry<String, Object> me : properties.entrySet()) {
            String key = me.getKey();
            Object val = me.getValue();
            if (val instanceof String) {
                String s = (String) val;
                e.setProperty(key, s.length() < 255 ? s : new Text(s));
            } else if (val instanceof Key) {
                e.setProperty(key, (Key) val);
            }
        }
        e.setProperty("id", createId(kind));
        e.setProperty("created", new Date().getTime());
        return dss.put(e);
    }

    public static List<Key> put(String kind, List list) {
        List<Key> keys = new ArrayList<Key>();
        for (Object o : list) {
            if (o instanceof Map) {
                keys.add(put(kind, (Map) o));
            } else if (o instanceof List) {
                throw new Error("Datastore does not support lists of lists.");
            } else {
                throw new Error("Datastore does not support lists with class = " + o.getClass() + "s.");
            }
        }
        return keys;
    }

/*
    public static Key put(String kind, String json) {
        try {
            return put(kind, new JSONObject(json));
        } catch(JSONException x) {
            throw new Error(x);
        }
    }

    public static Key put(String kind, JSONObject jo) {
        return put(kind, json.toMap(jo));
    }

    public static List<Key> put(String kind, JSONArray ja) {
        List<Key> keys = new ArrayList<Key>();
        for (int i = 0; i < ja.length(); i++) {
            try {
                JSONObject jo = ja.getJSONObject(i);
                keys.add(put(kind, jo));
            } catch (JSONException e) {
                throw new Error(e);
            }
        }
        return keys;
    }
*/

    public static Iterable<Entity> get(String kind) {
        Query q = new Query(kind);
        return dss.prepare(q).asIterable();
    }

    public static Entity get(String kind, String id) {
        Query q = new Query(kind);
        q.addFilter("id", Query.FilterOperator.EQUAL, id);
        Iterator<Entity> ie = dss.prepare(q).asIterator();
        return ie.hasNext() ? ie.next() : null;
    }

    public static Iterable<Entity> get(String kind, Map<String, Object> filters) {
        Query q = new Query(kind);
        for (Map.Entry<String, Object> me : filters.entrySet()) {
            q.addFilter(me.getKey(), Query.FilterOperator.EQUAL, me.getValue());
        }
        return dss.prepare(q).asIterable();
    }

    public static Entity get(Key k) {
        try {
            return dss.get(k);
        } catch (EntityNotFoundException e) {
            return null;
        }
    }

    public static void delete(Key k) {
        if (k != null) {
            dss.delete(k);
        }
    }

    public static void delete(Entity e) {
        if (e != null) {
            delete(e.getKey());
        }
    }

    public static String createId(String kind) {
        int retry = 0;
        for (;;) {
            Map<String, Object> m = new HashMap<String, Object>();
            String id = strings.randomSafeString(8);
            m.put("id", id);
            Iterator<Entity> ie = store.get(kind, m).iterator();
            if (!ie.hasNext()) {
                return id;
            }
            if (retry++ > 16) {
                throw new Error("Something is seriously wrong;");
            }
        }
    }

    public static boolean setProperty(Entity e, String property_name, Object property_value) {
        if (e == null || property_name == null || property_value == null) {
            return false;
        }
        if (property_value instanceof String) {
            String string_value = (String) property_value;
            if (string_value.length() < 256) {
                e.setProperty(property_name, string_value);
            } else {
                e.setProperty(property_name, new Text(string_value));
            }
        }
        store.put(e);
        return true;
    }

    public static class blob {
        public static String createUploadUrl(String url) {
            return bss.createUploadUrl(url);
        }

        public static Map<String, BlobKey> getUploadedBlobs(HttpServletRequest req) {
            return bss.getUploadedBlobs(req);
        }

        public static String k2s(BlobKey k) {
            return k.getKeyString();
        }

        public static BlobKey s2k(String k) {
            return new BlobKey(k);
        }
    }
}
